package com.example.demo.utils;

import com.example.demo.exception.UnauthorizedException;
import org.apache.commons.io.IOUtils;
import org.springframework.util.StringUtils;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.security.KeyStore;
import java.security.SecureRandom;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.concurrent.TimeUnit;

import javax.net.ssl.KeyManager;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
/**
 * @author lyc
 * @date 2018/9/29.
 * https://gitee.com/www.lyc.com/IJPay/blob/master/src/main/java/com/jpay/util/HttpUtils.java
 * https://gitee.com/jfinal/jfinal-weixin/blob/master/src/main/java/com/jfinal/weixin/sdk/utils/HttpUtils.java
 * jfinal 源码
 */
public class HttpUtils {

    private HttpUtils() {
    }

    public static String get(String url) {
        return delegate.get(url);
    }
    public static String get(String url, Map<String, String> queryParas) {
        return delegate.get(url, queryParas);
    }
    public static String get(String url, Map<String, String> queryParas,Map<String,String> header) {
        return delegate.get(url, queryParas,header);
    }
    public static String post(String url, String data) {
        return delegate.post(url, data);
    }
    public static String post(String url, Map<String, String> queryParas) {
        return delegate.post(url, queryParas,null);
    }
    public static String post(String url, Map<String, String> queryParas,Map<String,String> header) {
        return delegate.post(url, queryParas,header);
    }
    public static String postSSL(String url, String data, String certPath, String certPass) {
        return delegate.postSSL(url, data, certPath, certPass);
    }
    public static String postSSL(String url, String data, InputStream certFile, String certPass) {
        return delegate.postSSL(url, data, certPass, certFile);
    }
    public static InputStream download(String url, String params) {
        return delegate.download(url, params);
    }
    public static String upload(String url, File file, String params) {
        return delegate.upload(url, file, params);
    }

    /**
     * http请求工具 委托 优先使用OkHttp 最后使用JFinal HttpKit
     */
    private interface HttpDelegate {
        String get(String url);

        String get(String url, Map<String, String> queryParas);

        String get(String url, Map<String, String> queryParas, Map<String, String> header);

        String post(String url, String data);

        String post(String url, Map<String, String> queryParas, Map<String, String> header);

        String postSSL(String url, String data, String certPath, String certPass);

        String postSSL(String url, String data, String certPass, InputStream certFile);

        InputStream download(String url, String params);

        String upload(String url, File file, String params);
    }

    // http请求工具代理对象
    private static final HttpDelegate delegate;

    static {
        HttpDelegate delegateToUse = null;
        // okhttp3.OkHttpClient?
        if (ClassUtils.isPresent("okhttp3.OkHttpClient", HttpUtils.class.getClassLoader())) {
            delegateToUse = new OkHttp3Delegate();
        }

        delegate = delegateToUse;
    }

    /**
     * OkHttp3代理
     */
    private static class OkHttp3Delegate implements HttpDelegate {
        private okhttp3.OkHttpClient httpClient;

        public OkHttp3Delegate() {
            // 分别设置Http的连接,写入,读取的超时时间
            httpClient = new okhttp3.OkHttpClient().newBuilder().connectTimeout(10, TimeUnit.SECONDS)
                    .writeTimeout(10, TimeUnit.SECONDS).readTimeout(30, TimeUnit.SECONDS).build();
        }

        private static final okhttp3.MediaType CONTENT_TYPE_FORM = okhttp3.MediaType
                .parse("application/x-www-form-urlencoded");

        private String exec(okhttp3.Request request) {
            try {
                okhttp3.Response response = httpClient.newCall(request).execute();
                if(response.code() == 403){
                    throw new UnauthorizedException("没权限");
                }
                if (!response.isSuccessful())
                    throw new RuntimeException("Unexpected code " + response);

                return response.body().string();
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }

        @Override
        public String get(String url) {
            okhttp3.Request request = new okhttp3.Request.Builder().url(url).get().build();
            return exec(request);
        }

        @Override
        public String get(String url, Map<String, String> queryParas) {

            return get(url,queryParas,null);
        }

        @Override
        public String get(String url, Map<String, String> queryParas,Map<String,String> header) {
            okhttp3.HttpUrl.Builder urlBuilder = okhttp3.HttpUrl.parse(url).newBuilder();
            for (Entry<String, String> entry : queryParas.entrySet()) {
                urlBuilder.addQueryParameter(entry.getKey(), entry.getValue());
            }
            okhttp3.HttpUrl httpUrl = urlBuilder.build();
            okhttp3.Request request = null;
            if(header != null && !header.isEmpty()){
                request = new okhttp3.Request.Builder().url(httpUrl).addHeader(header.get("key"),header.get("value")).get().build();
            }else {
                request = new okhttp3.Request.Builder().url(httpUrl).get().build();
            }

            return exec(request);
        }

        @Override
        public String post(String url, String params) {
            okhttp3.RequestBody body = okhttp3.RequestBody.create(CONTENT_TYPE_FORM, params);
            okhttp3.Request request = new okhttp3.Request.Builder().url(url).post(body).build();
            return exec(request);
        }

        @Override
        public String postSSL(String url, String data, String certPass, InputStream certFile) {
            okhttp3.RequestBody body = okhttp3.RequestBody.create(CONTENT_TYPE_FORM, data);
            okhttp3.Request request = new okhttp3.Request.Builder().url(url).post(body).build();

            InputStream inputStream = certFile;
            try {
                KeyStore clientStore = KeyStore.getInstance("PKCS12");
                char[] passArray = certPass.toCharArray();
                clientStore.load(inputStream, passArray);

                KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
                kmf.init(clientStore, passArray);
                KeyManager[] kms = kmf.getKeyManagers();
                SSLContext sslContext = SSLContext.getInstance("TLSv1");

                sslContext.init(kms, null, new SecureRandom());

                @SuppressWarnings("deprecation")
                okhttp3.OkHttpClient httpsClient = new okhttp3.OkHttpClient().newBuilder()
                        .connectTimeout(10, TimeUnit.SECONDS).writeTimeout(10, TimeUnit.SECONDS)
                        .readTimeout(30, TimeUnit.SECONDS).sslSocketFactory(sslContext.getSocketFactory()).build();

                okhttp3.Response response = httpsClient.newCall(request).execute();

                if (!response.isSuccessful())
                    throw new RuntimeException("Unexpected code " + response);

                return response.body().string();
            } catch (Exception e) {
                throw new RuntimeException(e);
            } finally {
                IOUtils.closeQuietly(inputStream);
            }
        }

        @Override
        public String postSSL(String url, String data, String certPath, String certPass) {
            okhttp3.RequestBody body = okhttp3.RequestBody.create(CONTENT_TYPE_FORM, data);
            okhttp3.Request request = new okhttp3.Request.Builder().url(url).post(body).build();

            InputStream inputStream = null;
            try {
                KeyStore clientStore = KeyStore.getInstance("PKCS12");
                inputStream = new FileInputStream(certPath);
                char[] passArray = certPass.toCharArray();
                clientStore.load(inputStream, passArray);

                KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
                kmf.init(clientStore, passArray);
                KeyManager[] kms = kmf.getKeyManagers();
                SSLContext sslContext = SSLContext.getInstance("TLSv1");

                sslContext.init(kms, null, new SecureRandom());

                @SuppressWarnings("deprecation")
                okhttp3.OkHttpClient httpsClient = new okhttp3.OkHttpClient().newBuilder()
                        .connectTimeout(10, TimeUnit.SECONDS).writeTimeout(10, TimeUnit.SECONDS)
                        .readTimeout(30, TimeUnit.SECONDS).sslSocketFactory(sslContext.getSocketFactory()).build();

                okhttp3.Response response = httpsClient.newCall(request).execute();

                if (!response.isSuccessful())
                    throw new RuntimeException("Unexpected code " + response);

                return response.body().string();
            } catch (Exception e) {
                throw new RuntimeException(e);
            } finally {
                IOUtils.closeQuietly(inputStream);
            }
        }

        @Override
        public InputStream download(String url, String params) {
            okhttp3.Request request;
            if (!StringUtils.isEmpty(params)) {
                okhttp3.RequestBody body = okhttp3.RequestBody.create(CONTENT_TYPE_FORM, params);
                request = new okhttp3.Request.Builder().url(url).post(body).build();
            } else {
                request = new okhttp3.Request.Builder().url(url).get().build();
            }
            try {
                okhttp3.Response response = httpClient.newCall(request).execute();
                if(response.code() == 403){
                    throw new UnauthorizedException("没有权限");
                }
                if (!response.isSuccessful())
                    throw new RuntimeException("Unexpected code " + response);

                return response.body().byteStream();
            } catch (IOException e) {
                throw new RuntimeException(e);
            }

        }

        @Override
        public String upload(String url, File file, String params) {
            okhttp3.RequestBody fileBody = okhttp3.RequestBody
                    .create(okhttp3.MediaType.parse("application/octet-stream"), file);

            okhttp3.MultipartBody.Builder builder = new okhttp3.MultipartBody.Builder()
                    .setType(okhttp3.MultipartBody.FORM).addFormDataPart("media", file.getName(), fileBody);

            if (!StringUtils.isEmpty(params)) {
                builder.addFormDataPart("description", params);
            }

            okhttp3.RequestBody requestBody = builder.build();
            okhttp3.Request request = new okhttp3.Request.Builder().url(url).post(requestBody).build();

            return exec(request);
        }

        @Override
        public String post(String url, Map<String, String> params,Map<String,String> header) {
            okhttp3.FormBody.Builder builder = new okhttp3.FormBody.Builder();
            if (params == null) {
                params = new HashMap<String, String>();
            }
            if (params != null) {
                Set<Entry<String, String>> entries = params.entrySet();
                for (Entry<String, String> entry : entries) {
                    builder.add(entry.getKey(), entry.getValue());
                }
            }
            okhttp3.Request request = null;
            if(header != null && !header.isEmpty()){
                request  = new okhttp3.Request.Builder().url(url).addHeader(header.get("key"),header.get("value")).post(builder.build()).build();
            }else {
               request  = new okhttp3.Request.Builder().url(url).post(builder.build()).build();
            }
            return exec(request);
        }

    }
}